//Source file: D:\\WTK104\\apps\\Toddtris\\src\\com\\toddsoft\\toddtris\\midlet\\ToddtrisCanvas.java

package com.toddsoft.toddtris.midlet;

import javax.microedition.lcdui.Canvas;
import com.toddsoft.toddtris.model.WellListener;
import com.toddsoft.toddtris.model.Block;
import com.toddsoft.toddtris.model.Well;
import com.toddsoft.toddtris.model.HighScoreTable;
import com.toddsoft.toddtris.control.RepeatController;

import javax.microedition.lcdui.Graphics;

public class ToddtrisCanvas extends Canvas implements WellListener {
    private static final int BLANK_COLOUR = 0x00ffffff;
    private static final int GRID_COLOUR = 0x00b0e8ee;
    private static final int GRID_COLOUR_GREY = 0x00000099;
    private static final int BACKGROUND_COLOUR = 0x00000000;
    private static final int GAME_OVER_COLOUR = 0x00ff0000;
    private static final int BLOCK_COLOURS_1[] =      {0x0000ff00,0x000000ff,0x00ff0000,0x00aaaaaa,0x00ff00ff,0x0000ffff,0x00cccc00};
    private static final int BLOCK_COLOURS_2[] =      {0x00ff0000,0x0000ff00,0x00aaaaaa,0x0000ffff,0x00cccc00,0x00ff00ff,0x000000ff};
    private static final int DEAD_BLOCK_COLOURS_1[] = {0x00009900,0x00000099,0x00990000,0x00777777,0x00990099,0x00009999,0x00888800};
    private static final int DEAD_BLOCK_COLOURS_2[] = {0x00990000,0x00009900,0x00777777,0x00009999,0x00888800,0x00990099,0x00000099};
    private static final int BLOCK_GREY_1 = 0x00009900;
    private static final int BLOCK_GREY_2 = 0x00000099;
    private static final int DEAD_BLOCK_GREY_1 = 0x00005500;
    private static final int DEAD_BLOCK_GREY_2 = 0x00000055;
    private static final int X_OFFSET = 10;
    private static final int Y_OFFSET = 0;
    private int screenWidth;
    private int screenHeight;
    private int nextBlockX;
    private int nextBlockY;
    private int scoreX;
    private int scoreY;
    
    /**
     * the well as it appears on screen
     */
    private int screenWell[][] = new int [Well.WELL_WIDTH][Well.WELL_HEIGHT] ;
    
    /**
     * the score as displayed on the screen
     */
    private int screenLines = - 1;

    /**
     * the next block as displayed on the screen
     */
    private Block screenNextBlock;


    private int blockSize;
    
    /**
     * is a full redraw required on next paint?
     */
    private boolean fullRedraw = true;
    private boolean useColour;
    private Well well;
    private RepeatController controller;
    private KeyHandler keyHandler;
    private HighScoreTable[] hsta;
    private boolean showGrid;
    
    /**
     * @param well
     * @param ctrl
     * @param keys
     * @param useColour
     * @param sens
     */
    public ToddtrisCanvas(Well well, 
                          RepeatController ctrl, 
                          int[] keys, 
                          boolean useColour, 
                          boolean showGrid,
                          HighScoreTable[] hsta) {

        this.setKeys(keys);

        this.useColour = useColour;

        this.showGrid = showGrid;

        this.well = well;

        this.hsta = hsta;

        screenWidth  = getWidth();
        screenHeight = getHeight();

        /* work out the size of a block */
        int maxHeight = screenHeight / Well.WELL_HEIGHT;

        /* allow size for left offset and for next block to show on right */
        int maxWidth = screenWidth - (X_OFFSET / (Well.WELL_WIDTH + 6));

        blockSize = (maxHeight < maxWidth) ? maxHeight : maxWidth;

        nextBlockX = X_OFFSET + ((Well.WELL_WIDTH + 1) * blockSize);
        nextBlockY = blockSize; 

        scoreX = nextBlockX;
        scoreY = nextBlockY + (6 * blockSize);

        /* listener for well events */
        well.addListener(this);

        this.controller = ctrl;
    }
    
    /**
     * Notification handler when canvas hidden.
     * Forces a redraw when canvas is shown again.
     * Signals that the game is paused while the canvas is obscured.
     */
    protected void hideNotify() {
        this.scheduleFullRedraw();     
    }

    /*
     * Turns grid on or off
     */
    void setGrid(boolean show) {
        this.showGrid = show;
        this.scheduleFullRedraw();
    }
    
    boolean isGridShowing() {
        return this.showGrid;
    }

    /**
     * @param keyCode
     */
    public void keyPressed(int keyCode) {

        int translated = keyHandler.translateKeyCode(keyCode);

        /* check for the particular key presses */
        switch (translated) {
        case KeyHandler.CLOCKWISE:
            controller.rotate();
            break;
        case KeyHandler.ANTICLOCKWISE:
            controller.antirotate();
            break;
        case KeyHandler.DROP:
            controller.drop();
            break;
        case KeyHandler.LEFT:
            controller.startLeft();
            break;
        case KeyHandler.RIGHT:
            controller.startRight();
            break;
        case KeyHandler.DOWN:
            controller.startDown();
            break;
        }     
    }
    
    /**
     * @param keyCode
     */
    public void keyReleased(int keyCode) {

        int translated = keyHandler.translateKeyCode(keyCode);

        /* check for the particular key presses */
        switch (translated) {
        case KeyHandler.LEFT:
            controller.stopLeft();
            break;
        case KeyHandler.RIGHT:
            controller.stopRight();
            break;
        case KeyHandler.DOWN:
            controller.stopDown();
            break;
        }     
    }
    
    void scheduleFullRedraw() {
        this.fullRedraw = true;     
    }
    
    /**
     * Paint the well pit and its components
     * @param g graphics object to be rendered
     */
    public void paint(Graphics g) {
        if (fullRedraw) {
            g.setColor(BACKGROUND_COLOUR);
            g.fillRect(0,
                       0,
                       getWidth(),
                       getHeight());
        }

        /* draw all the blocks */
        for (int i = 0; i < Well.WELL_WIDTH; i++ ) {
            for (int j = 0; j < Well.WELL_HEIGHT; j++ ) {
                int elem = well.getElem(i, j);
                if (fullRedraw || (elem != screenWell[i][j])) {
                    drawWellSquare(elem, i, j, g);
                }
                screenWell[i][j] = elem;
            }
        }

        /* if the next block has changed, update it */
        Block nextBlock = well.getNextBlock();
        if (fullRedraw || (nextBlock != screenNextBlock)) {
            g.setColor(BLANK_COLOUR);

            /* put a white rectangle around the next block */
            g.fillRect((nextBlockX - (blockSize / 2)), 
                       (nextBlockY - (blockSize / 2)), 
                       blockSize * 5, 
                       blockSize * 5);
            int blockNum;

            /* add an offset to the squares location if size is less than 4 */
            int addOffset = ((4 - nextBlock.getSize())* blockSize) / 2;

            for (int i = 0; i < nextBlock.getSize(); i++) {
                for (int j = 0; j < nextBlock.getSize(); j++) {
                    if ((blockNum = nextBlock.getElem(i, j)) != 0) {

                        int xPos = addOffset + nextBlockX + i*blockSize;
                        int yPos = addOffset + nextBlockY + j*blockSize;
                        this.drawSquare(blockNum, xPos, yPos, g);
                    }
                }
            }
            screenNextBlock = nextBlock;
        }

        /* if doing a full redraw, show the high score */
        if (fullRedraw) {

            int highScore = HighScoreTable.getCurrentHighScore(well,
                                                               hsta);

            String highScoreString = null;
            if (highScore == 0) {
                highScoreString = "Hi. -";
            } else {
                if (well.getTargetLines() == 0) {
                    highScoreString = "Hi." + highScore;
                } else {
                    highScoreString = "Hi." + formatTime(highScore);
                }
            }
            g.setColor(BLANK_COLOUR);
            g.drawString(highScoreString, 
                         scoreX, 
                         blockSize * Well.WELL_HEIGHT, 
                         Graphics.BOTTOM | Graphics.LEFT);
        }

        /*
         * show the time 
         */

        /* blank out the old time */
        g.setColor(BACKGROUND_COLOUR);
        g.fillRect(scoreX, 
                   (blockSize * Well.WELL_HEIGHT) - (2 * g.getFont().getHeight()), 
                   (screenWidth - scoreX),
                   g.getFont().getHeight());
        g.setColor(BLANK_COLOUR);
        /* write the new one */
        g.drawString(formatTime((int)well.getRunningTimeInMillis() / 1000), 
                     scoreX, 
                     (blockSize * Well.WELL_HEIGHT) 
                                - g.getFont().getHeight(), 
                     Graphics.BOTTOM | Graphics.LEFT);

        /* if the number of lines has changed, update it */
        int lines = (well.getTargetLines() == 0)
                    ? well.getLines()               //marathon
                    : well.isComplete()             //time trial
                            ? 0                     //don't show a -ve
                            : well.getTargetLines() 
                                - well.getLines();  //remaining lines

        if (fullRedraw || (screenLines != lines)) {
            /* blank out the old score */
            g.setColor(BACKGROUND_COLOUR);
            g.fillRect(scoreX, 
                       scoreY, 
                       (screenWidth - scoreX), 
                       g.getFont().getHeight());

            g.setColor(BLANK_COLOUR);
            g.drawString("" + lines, 
                         scoreX, 
                         scoreY, 
                         Graphics.TOP | Graphics.LEFT);

            screenLines = lines;
        }

        /* if game is over, plaster a game over message on screen */
        if (well.isGameOver()) {
            g.setColor(GAME_OVER_COLOUR);

            /* put text in middle of well */
            g.drawString("Game", 
                         ToddtrisCanvas.X_OFFSET + ((Well.WELL_WIDTH * blockSize) / 2),
                         this.getHeight() / 2,
                         Graphics.BOTTOM | Graphics.HCENTER);

            g.drawString("Over", 
                         ToddtrisCanvas.X_OFFSET + ((Well.WELL_WIDTH * blockSize) / 2),
                         this.getHeight() / 2,
                         Graphics.TOP | Graphics.HCENTER);

        } else if (well.isComplete()) {
            g.setColor(GAME_OVER_COLOUR);

            /* put text in middle of well */
            g.drawString("Well", 
                         ToddtrisCanvas.X_OFFSET + ((Well.WELL_WIDTH * blockSize) / 2),
                         this.getHeight() / 2,
                         Graphics.BOTTOM | Graphics.HCENTER);

            g.drawString("Done", 
                         ToddtrisCanvas.X_OFFSET + ((Well.WELL_WIDTH * blockSize) / 2),
                         this.getHeight() / 2,
                         Graphics.TOP | Graphics.HCENTER);
        }

        /* full redraw is not required */
        fullRedraw = false;     
    }

    private String formatTime(int secs) {
        if ((secs % 60) < 10) {
            return (int)(secs / 60) + ":0" + (secs % 60);
        } else {
            return (int)(secs / 60) + ":" + (secs % 60);
        }
    }
    
    /**
     * @param blockNum
     * @param wellX
     * @param wellY
     * @param gr
     */
    private void drawWellSquare(int blockNum, int wellX, int wellY, Graphics gr) {
        /* calculate absolute positions */
        int x = X_OFFSET + (wellX * blockSize);
        int y = Y_OFFSET + (wellY * blockSize);
        drawSquare(blockNum, x, y, gr);     
    }
    
    /**
     * @param blockNum
     * @param x
     * @param y
     * @param gr
     */
    private void drawSquare(int blockNum, int x, int y, Graphics gr) {

        if (blockNum == 0) {
            this.drawBlankSquare(x, y, gr);
        } else {

            /*
             * If the block number is -8, then draw a grey square even if it
             * is a colour device
             */
            if (this.useColour && (blockNum != -8)) {
                this.drawColourSquare(blockNum, x, y, gr);
            } else {
                this.drawGreySquare(blockNum, x, y, gr);
            }
        }     
    }

    /**
     * @param blockNum
     * @param x
     * @param y
     * @param gr
     */
    private void drawBlankSquare(int x, int y, Graphics gr) {

        if (this.showGrid) {
            // draw the grid
            if (this.useColour) {
                gr.setColor(GRID_COLOUR);
            } else {
                gr.setColor(GRID_COLOUR_GREY);
            }
        } else {
            // grid in same colour as rest of background
            gr.setColor(BLANK_COLOUR);
        }
        gr.drawLine(x,
                    y,
                    x,
                    y + blockSize - 1);     

        gr.drawLine(x,
                    y + blockSize - 1,
                    x + blockSize - 1,
                    y + blockSize - 1);     

        gr.setColor(ToddtrisCanvas.BLANK_COLOUR);
        gr.fillRect(x + 1,
                    y,
                    blockSize - 1,
                    blockSize - 1);

    }

    
    /**
     * @param blockNum
     * @param x
     * @param y
     * @param gr
     */
    private void drawColourSquare(int blockNum, int x, int y, Graphics gr) {
        int [] colours1;
        int [] colours2;
        int colourIndex;
        
        if (blockNum < 0) {
            colourIndex = (0 - blockNum) - 1;
            colours1 = DEAD_BLOCK_COLOURS_1;
            colours2 = DEAD_BLOCK_COLOURS_2;
        } else {
            colourIndex = blockNum - 1;
            colours1 = BLOCK_COLOURS_1;
            colours2 = BLOCK_COLOURS_2;
        }

        this.drawSquare(colours1[colourIndex], 
                        colours2[colourIndex],
                        x,
                        y,
                        gr);     
    }
    
    /**
     * @param blockNum
     * @param x
     * @param y
     * @param gr
     */
    private void drawGreySquare(int blockNum, int x, int y, Graphics gr) {

        int innerColour;
        int outerColour;

        if (blockNum < 0) {
            innerColour = DEAD_BLOCK_GREY_1;
            outerColour = DEAD_BLOCK_GREY_2;
        } else {
            innerColour = BLOCK_GREY_1;
            outerColour = BLOCK_GREY_2;
        }

        this.drawSquare(innerColour,outerColour, x, y, gr);     
    }
    
    /**
     * @param innerColour
     * @param outerColour
     * @param x
     * @param y
     * @param gr
     */
    private void drawSquare(int innerColour, int outerColour, int x, int y, Graphics gr) {

        gr.setColor(innerColour);
        gr.fillRect(x + 1,
                    y + 1,
                    blockSize - 2,
                    blockSize - 2);

        gr.setColor(outerColour);
        gr.drawRect(x,
                    y,
                    blockSize - 1,
                    blockSize - 1);     
    }
    
    /**
     * @param keys
     */
    void setKeys(int[] keys) {
        keyHandler = KeyHandler.getKeyHandler(keys, this);     
    }
    
    /**
     * @param well
     */
    public void wellChanged(Well well) {
        repaint();     
    }
    
    /**
     * @param well
     * @param linesMade
     */
    public void madeLines(Well well, int linesMade) {
        /* do nothing - wellChanged deals with repaint */
    }
    
    /**
     * @param well
     */
    public void gameOver(Well well) {
        repaint();     
    }
    
    /**
     * @param well
     */
    public void wellReset(Well well) {

        /* do a full redraw */
        this.scheduleFullRedraw();
        repaint();     
    }
    
    /**
     * @param well
     */
    public void blockLanded(Well well) {
        /* do nothing - wellChanged deals with repaint */
    }
    
    /**
     * @param well
     */
    public void complete(Well well) {
        repaint();     
    }
}
